﻿using System;
using System.ComponentModel;
using System.Threading;

namespace WcfServiceLibrary
{
    public class AsyncResult<T, TResult> : IDisposable, IAsyncResult
    {
        private volatile Boolean _isCompleted;
        private ManualResetEvent _manualResetEvent;
        private AsyncCallback _asyncCallback;
        private TResult _result;
        private Exception _exception;
        private readonly T _parameteres;

        public AsyncResult(Func<T, TResult> workToBeDone, T parameteres, AsyncCallback callback, Object state)
        {
            IsDisposed = false;
            _asyncCallback = callback;
            AsyncState = state;
            _parameteres = parameteres;
            QueueWorkOnThreadPool(workToBeDone);
        }

        private void QueueWorkOnThreadPool(Func<T, TResult> workToBeDone)
        {
            ThreadPool.QueueUserWorkItem(state =>
                                             {
                                                 try
                                                 {
                                                     _result = workToBeDone(_parameteres);
                                                 }
                                                 catch (Exception ex)
                                                 {
                                                     _exception = ex;
                                                 }
                                                 finally
                                                 {
                                                     UpdateStatusToComplete();
                                                     NotifyCallbackWhenAvailable();
                                                 }
                                             });
        }

        public TResult FetchResultsFromAsyncOperation()
        {
            if (!_isCompleted)
            {
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
            }
            if (_exception != null)
            {
                throw _exception;
            }
            return _result;
        }

        private void NotifyCallbackWhenAvailable()
        {
            if (_asyncCallback != null)
            {
                _asyncCallback(this);
            }
        }

        public object AsyncState { get; private set; }

        public WaitHandle AsyncWaitHandle
        {
            get { return GetEventHandle(); }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return _isCompleted; }
        }

        private readonly Object _locker = new Object();

        private ManualResetEvent GetEventHandle()
        {
            lock (_locker)
            {
                if (_manualResetEvent == null)
                {
                    _manualResetEvent = new ManualResetEvent(false);
                }
                if (_isCompleted)
                {
                    _manualResetEvent.Set();
                }
            }
            return _manualResetEvent;
        }

        private void UpdateStatusToComplete()
        {
            _isCompleted = true;
            lock (_locker)
            {
                if (_manualResetEvent != null)
                {
                    _manualResetEvent.Set();
                }
            }
        }

        #region IDisposable Members

        [Browsable(false)]
        public bool IsDisposed { get; private set; }

        public void Dispose()
        {
            if (IsDisposed)
            {
                return;
            }
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            try
            {
                if (disposing)
                {
                    _manualResetEvent.Close();
                    _manualResetEvent = null;
                    AsyncState = null;
                    _asyncCallback = null;
                }
            }
            finally
            {
                IsDisposed = true;
            }
        }

        #endregion IDisposable Members
    }
}
